package com.partner.match.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.partner.match.common.ErrorCode;
import com.partner.match.constant.UserConstant;
import com.partner.match.exception.BusinessException;
import com.partner.match.mapper.UserMapper;
import com.partner.match.mapper.UserTeamMapper;
import com.partner.match.model.entity.User;
import com.partner.match.model.entity.UserTeam;
import com.partner.match.model.enums.UserRoleEnum;
import com.partner.match.model.vo.LoginUserVO;
import com.partner.match.model.vo.UserVO;
import com.partner.match.service.UserService;
import com.partner.match.utils.AlgorithmUtils;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.partner.match.constant.UserConstant.SALT;

/**
 * @Author: jzz
 * @CreateTime: 2025-01-11
 * @Description: UserServiceImpl
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserTeamMapper userTeamMapper;


    @Override
    public List<UserVO> getUserTagsList(List<String> tagsList) {
        if (CollUtil.isEmpty(tagsList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        // 2.使用内存进行查询
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        List<User> userList = userMapper.selectList(userQueryWrapper);
        // gson 工具，反序列化
        Gson gson = new Gson();
        return userList.stream().filter(user -> {
            String userTags = user.getTags();
            Set<String> tempUserTags = gson.fromJson(userTags, new TypeToken<Set<String>>() {
            }.getType());
            // 使用optional解决空指针问题
            tempUserTags = Optional.ofNullable(tempUserTags).orElse(new HashSet<>());
            for (String tagName : tagsList) {
                if (!tempUserTags.contains(tagName)) {
                    return false;
                }
            }
            return true;
        }).map(user -> BeanUtil.copyProperties(user, UserVO.class)).collect(Collectors.toList());

    }

    @Override
    public Page<User> recommendUsers(long currentPage, long pageSize, HttpServletRequest request) {
        // 检测用户是否登录
        LoginUserVO loginUserVO = this.getCurrentUser(request);
        String redisKey = String.format("user:recommend:%s", loginUserVO.getUserId());
        // 如果有缓存，首先查询缓存内数据
        Page<User> userPage = (Page<User>) redisTemplate.opsForValue().get(redisKey);
        if (userPage != null) {
            return userPage;
        }
        QueryWrapper<User> userQW = new QueryWrapper<>();
        userPage = this.page(new Page<>(currentPage, pageSize), userQW);
        if (CollUtil.isEmpty(userPage.getRecords())) {
            Collections.emptyList();
        }
        try {
            redisTemplate.opsForValue().set(redisKey, userPage, 1, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("redis set key error", e);
        }
        return userPage;
    }

    @Override
    public Long registerUser(String userName, String userPassword, String userAccount, String checkPassword,String tags) {
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账户过短");
        }
        if (userPassword.length() < 6) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码过短");
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        synchronized (userAccount.intern()) {
            // 校验账户是否重复
            QueryWrapper<User> userQW = new QueryWrapper<>();
            userQW.eq(User.USER_ACCOUNT, userAccount);
            Long count = userMapper.selectCount(userQW);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户重复");
            }
            // 密码加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            User user = new User();
            user.setUserName(userName);
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            user.setTags(tags);
            boolean saved = this.save(user);
            if (!saved) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "注册失败");
            }
            return user.getUserId();
        }
    }

    @Override
    public LoginUserVO login(String userAccount, String userPassword, HttpServletRequest request) {
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户错误");
        }
        if (userPassword.length() < 6) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
        }
        // 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 校验用户是否存在
        QueryWrapper<User> userQW = new QueryWrapper<>();
        userQW.eq(User.USER_ACCOUNT, userAccount);
        userQW.eq(User.USER_PASSWORD, encryptPassword);
        User user = userMapper.selectOne(userQW);
        if (user == null) {
            log.info("用户登录失败");
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "账户或密码错误");
        }
        // 记录用户信息
        StpUtil.login(user.getUserId());
        StpUtil.getSession().set(UserConstant.USER_LOGIN_STATE, user);
        return BeanUtil.copyProperties(user, LoginUserVO.class);
    }

    @Override
    public LoginUserVO getCurrentUser(HttpServletRequest request) {
        User user = (User) StpUtil.getSession().get(UserConstant.USER_LOGIN_STATE);
        if (user == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        QueryWrapper<UserTeam> userTeamQW = new QueryWrapper<>();
        userTeamQW.select(UserTeam.TEAM_ID);
        userTeamQW.eq(UserTeam.USER_ID,user.getUserId());
        List<Long> teamIds = userTeamMapper.selectList(userTeamQW).stream().map(item -> item.getTeamId()).collect(Collectors.toList());
        user.setHasJoinList(teamIds);
        return BeanUtil.copyProperties(user, LoginUserVO.class);
    }

    @Override
    public Boolean updateUser(User user, HttpServletRequest request) {
        User currentUser = (User) StpUtil.getSession().get(UserConstant.USER_LOGIN_STATE);
        if (currentUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        Long userId = user.getUserId();
        System.out.println("userId:" + userId);
        System.out.println("currentUserId:" + currentUser.getUserId());
        // 只有管理员和当前用户可以修改信息
        if (isAdmin(currentUser) || Objects.equals(userId, user.getUserId())) {
            return this.updateById(user);
        }
        return false;

    }

    @Override
    public List<UserVO> matchUsers(Long num) {
        User loginUser = (User) StpUtil.getSession().get(UserConstant.USER_LOGIN_STATE);
        // 获取登录用户的标签
        String tags = loginUser.getTags();
        String redisKey = String.format("user:match:%s", loginUser.getUserId());
        List<UserVO> userVOS = (List<UserVO>) redisTemplate.opsForValue().get(redisKey);
        if(userVOS != null){
            return userVOS;
        }

        // 剔除自己，获取所有用户
        QueryWrapper<User> userQW = new QueryWrapper<>();
        // userQW.select(User.USER_ID, User.TAGS);
        userQW.ne(User.USER_ID, loginUser.getUserId());
        // userQW.isNotNull(User.TAGS);
        List<User> userList = this.list(userQW);

        // 将tags json字符串转化为列表
        Gson gson = new Gson();
        List<String> tagList = gson.fromJson(tags, new TypeToken<List<String>>() {
        }.getType());

        // 存储相似用户和用户相似度
        List<Pair<User,Long>> pairList = new ArrayList<>();

        // 遍历所有用户，调用距离编辑算法，将相似度存储在indexDistanceMap
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            String userTags = user.getTags();
            if (StrUtil.isBlank(userTags)) {
                continue;
            }
            List<String> userTagList = gson.fromJson(userTags, new TypeToken<List<String>>() {
            }.getType());
            long distance = AlgorithmUtils.minTagDistance(tagList, userTagList);
            pairList.add(new Pair<>(user,distance));
        }

        List<Pair<User, Long>> topUserPairList = pairList.stream()
                .sorted((a, b) -> (int) (a.getValue() - b.getValue()))
                .limit(num)
                .collect(Collectors.toList());
        userVOS = topUserPairList.stream()
                .map(pair -> BeanUtil.copyProperties(pair.getKey(), UserVO.class))
                .collect(Collectors.toList());
        try {
            // 设置缓存
            redisTemplate.opsForValue().set(redisKey,userVOS,1,TimeUnit.MINUTES);
        }catch (Exception e){
            log.error("redis set key error", e);
        }

        // 数据脱敏并返回num个用户
        return userVOS;
    }

    /**
     * 查询用户是否为管理员
     *
     * @param user
     * @return
     */
    private Boolean isAdmin(User user) {
        Integer userRole = user.getUserRole();
        return Objects.equals(userRole, UserRoleEnum.ADMIN.getValue());
    }

    /**
     * 通过sql查询用户列表
     *
     * @param tagsList
     * @return
     */
    @Deprecated
    private List<UserVO> getUserTagsListBySQL(List<String> tagsList) {
        if (CollUtil.isEmpty(tagsList)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        for (String tag : tagsList) {
            userQueryWrapper = userQueryWrapper.like(User.TAGS, tag);
        }
        List<User> users = userMapper.selectList(userQueryWrapper);
        return users.stream().map(user -> BeanUtil.copyProperties(user, UserVO.class)).collect(Collectors.toList());
    }
}
